;(function(root, factory){
    if (typeof exports === "object") {
        // CommonJS
        module.exports = exports = factory();
    }
    else if (typeof define === "function" && define.amd) {
        // AMD
        define([], factory);
    }
    else {
        // Global (browser)
        root.ScriptLoader = factory();
    }
})(this, function(){
    // 加载节点
    function Node(d) {
        // 依赖的子节点
        var child = [];
        // 已加载完成的子节点数
        var received = 0;
        // 存储的数据
        var data = d
        // 依赖该节点的父节点
        var parent = [];
        // 加载该节点的方法
        var arriveFunc = () => {};
        var rst = {
            data: data,
            getChild: (i) => child[i],
            getParent: () => parent,
            appendParent: (p) => parent.push(p),
            setArriveFunc: (func) => arriveFunc = func,
            append: (node) => {
                child.push(node);
                node.appendParent(rst);
            },
            childCount: () => child.length,
            // 子节点加载完成后调用
            prepare: () => {
                // 子加载完成
                received++;
                // 子全部加载完成， 加载自身
                if (received >= child.length) {
                    arriveFunc(rst)
                }
            },
            // 本节点加载完成调用
            arrive: () => {
                // 加载完成后通知父节点
                for (var i = 0; i < parent.length; i++) {
                    parent[i].prepare()
                }
            },
        };
        return rst;
    }
    // 通过name寻找节点
    function findByName(scripts, n){
        for (var i = 0; i < scripts.length; i++) {
            if(scripts[i].name == n) {
                return scripts[i];
            }
        }
        throw "no such item in list";
    }
    // 创建节点
    function createNode(scripts, name, func) {
        var script = findByName(scripts, name);
        if (script.node != undefined) {
            return script.node;
        }
        script.node = new Node(script);
        // 设置加载方法
        script.node.setArriveFunc(func);
        var before = script.before;
        // 存在依赖的节点
        if (!before) {
            return script.node;
        }
        // 构建关系
        for (var i = 0; i < before.length; i++) {
            var node = createNode(scripts, before[i], func);
            script.node.append(node);
        }
        return script.node;
    }

    // 根据列表数据转化成节点
    function toNodes(scripts, func) {
        var nodes = [];
        for (var i = 0; i < scripts.length; i++) {
            nodes.push(createNode(scripts, scripts[i].name, func));
        }
        return nodes;
    }

    // 寻找叶节点（即不存在依赖关系的节点）
    function findLeafNode(nodes) {
        var leafNodes = [];
        for (var i = 0; i < nodes.length; i++) {
            const node = nodes[i];
            if(node.childCount() == 0) {
                leafNodes.push(node);
            }
        }
        return leafNodes;
    }

    // ajax get请求
    function httpGet(url, asyn, success) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url, asyn);
        xhr.onload = function (e) {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    success(xhr.responseText);
                } else {
                    console.error(xhr.statusText);
                }
            }
        };
        xhr.onerror = function (e) {
            console.error(xhr.statusText);
        };
        xhr.send(null);
    }

    function ScriptLoader() {
        var loader = {};
        var res = [];
        // 全部加载完成后触发
        var loadAll = () => {};
        // 加载函数
        function prepare(node) {
            const {type = "text/javascript", src = "", id = "", innerText = false} = node.data;
            // 该类型为全部加载的触发函数
            if (type == "loadAllfunction") {
                loadAll();
                return;
            }
            // src为空不处理，直接标记加载完成
            if (src === "") {
                node.arrive();
                return;
            }

            var script = document.createElement("script");
            script.setAttribute("type", type);
            if (id !== "") {
                script.setAttribute("id", id);
            }
            // 如果是将脚本内容引入文件
            if (innerText) {
                // 通过ajax加载内容
                httpGet(src, true, function(text) {
                    script.innerHTML = text;
                    // 标记加载完成
                    node.arrive();
                });
            }
            else {
                // 一般引用方式
                script.setAttribute("src", src);
                script.onload = function(){
                    // 标记加载完成
                    node.arrive();
                };
            }
            document.body.appendChild(script);
        }
      
        // 触发加载的操作
        loader.load = function() {
            var arr = [];
            for(var i = 0, l = res.length; i < l; i++) {
                if (res[i].name == undefined) {
                    const index = res[i].src.lastIndexOf("/");
                    if (index == -1) {
                        res[i].name = res[i].src;
                    }
                    else {
                        res[i].name = res[i].src.substring(index+1);
                    }
                }
                arr.push(res[i].name);
            }
            // 构造全部加载完成后触发的方法
            res.push({type: "loadAllfunction", src: "function", before: arr});
            
            // 将数据转化成图节点
            const nodes = toNodes(res, prepare);
            // 寻找叶节点
            const leafNodes = findLeafNode(nodes);
            const len = leafNodes.length;
            // 加载叶节点
            for(var i = 0; i < len; i++) {
                leafNodes[i].prepare();
            }
        }

        // 设置全部加载完成后的方法
        loader.loadAll = function(all) {
            loadAll = all;
            return loader;
        }
        // 添加要加载的数据
        loader.add = function(r) {
            if (Array.isArray(r)) {
                res.push(...r);
            }
            else {
                res.push(r);
            }
            return loader;
        }
        return loader;
    }
    return ScriptLoader;
})